iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0
Software Development

《醫資生的 FHIR 30日入門:用 Postman 讀懂醫療資料交換》系列 第 14

Day 14|GET、POST、PUT、DELETE有什麼不同?

  • 分享至 

  • xImage
  •  

前言

在上一篇文章中,我們使用餐廳點餐的方式認識了REST API,也了解一次API溝通通常包含:

  • Client
  • Server
  • Request
  • Response
  • Endpoint
  • Headers
  • Body
  • Status Code

今天要進一步認識HTTP中常見的四種方法:

  • GET
  • POST
  • PUT
  • DELETE

在FHIR RESTful API中,它們可以用來讀取、搜尋、新增、更新及刪除Resource。

不過,四種方法不只名稱不同,它們使用的URL、Request Body、成功狀態碼,以及重複送出後的結果也可能不同。

本文使用的網址、病人及醫療資料皆為虛構教學範例。實際操作時,請勿將真實病人資料傳送至公開測試伺服器。


先用資料管理理解四種方法

假設FHIR Server中保存一筆Patient Resource。

我們可能會想執行以下操作:

需求 HTTP方法
讀取一筆Patient GET
搜尋Patient GET
建立新的Patient POST
更新既有Patient PUT
刪除Patient DELETE

可以先用一句話記住:

GET讀取、POST新增、PUT更新、DELETE刪除。

但實際規則比這句話更完整,接下來分別介紹。


一、GET:讀取或搜尋資料

GET用來向Server取得資料。

在FHIR中,GET常用於:

  • 讀取一筆Resource
  • 搜尋符合條件的Resource
  • 查看Resource歷史版本
  • 查看FHIR Server能力

GET通常不使用Request Body,而是透過URL及查詢參數表達需求。


使用GET讀取一筆Patient

如果已經知道Patient的id,可以送出:

GET https://hospital.example.org/fhir/Patient/patient-001
Accept: application/fhir+json

URL可以拆成:

部分 內容
FHIR Server https://hospital.example.org/fhir
Resource類型 Patient
Resource id patient-001

整個Request代表:

請讀取id為patient-001的Patient Resource,並回傳FHIR JSON。

如果資料存在,Server可能回傳:

200 OK
Content-Type: application/fhir+json

Response Body:

{
  "resourceType": "Patient",
  "id": "patient-001",
  "active": true,
  "name": [
    {
      "text": "王小明"
    }
  ]
}

使用GET搜尋Patient

如果不知道Resource id,但知道病人姓名,可以使用搜尋:

GET https://hospital.example.org/fhir/Patient?name=王小明
Accept: application/fhir+json

其中:

?name=王小明

是搜尋參數。

搜尋結果通常不是直接回傳單一Patient,而是回傳Bundle Resource。

簡化範例如下:

{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 1,
  "entry": [
    {
      "resource": {
        "resourceType": "Patient",
        "id": "patient-001",
        "name": [
          {
            "text": "王小明"
          }
        ]
      }
    }
  ]
}

即使只找到一筆資料,FHIR搜尋結果仍通常會以Bundle呈現。Bundle會在Day 19詳細介紹。


GET會修改資料嗎?

正常的GET操作是用來讀取資料,不應因為執行GET而修改Resource。

例如,重複送出:

GET /Patient/patient-001

應該只是重複取得Patient,而不是建立新Patient或改變病人姓名。

這種不應改變Server資源狀態的HTTP方法,稱為Safe Method。

不過,Server仍可能留下存取Log、統計次數或更新快取,這些技術性紀錄不等於修改Client要求讀取的Patient內容。


二、POST:建立新的Resource

POST在FHIR中常用來建立新的Resource。

例如,要建立一筆Patient,可以送出:

POST https://hospital.example.org/fhir/Patient
Content-Type: application/fhir+json
Accept: application/fhir+json

Request Body:

{
  "resourceType": "Patient",
  "active": true,
  "name": [
    {
      "text": "王小明",
      "family": "王",
      "given": [
        "小明"
      ]
    }
  ],
  "gender": "male",
  "birthDate": "2000-01-01"
}

注意POST建立Resource時,URL通常停在Resource類型:

/Patient

而不是:

/Patient/patient-001

這是因為一般FHIR create操作會由Server分配新Resource的id。


POST成功後會得到什麼?

如果Patient建立成功,Server通常會回傳:

201 Created

Response Header可能包含:

Location: https://hospital.example.org/fhir/Patient/123/_history/1

這表示Server建立了一筆id為123的Patient,目前版本為1

依照Server的回應設定,Response Body也可能包含剛建立的Patient:

{
  "resourceType": "Patient",
  "id": "123",
  "meta": {
    "versionId": "1"
  },
  "active": true,
  "name": [
    {
      "text": "王小明"
    }
  ]
}

Client應該保存Server回傳的id,後續才能讀取或更新該Resource。


POST時誰決定id?

一般FHIR create操作中:

  • Client將Resource送到/Patient
  • Server建立Resource
  • Server分配Resource id
  • Server將結果回傳Client

所以Request Body通常不應依賴Client自行指定的id

即使Client傳送了某個id,Server也可能忽略或拒絕它,實際行為應依照FHIR規範及該Server的實作。

如果Client需要對指定id的位置進行更新或建立,通常會使用PUT,而不是一般POST create。


POST可以一直重複送嗎?

假設將相同的POST Request送出兩次:

POST /Patient

Server可能建立兩筆不同的Patient:

Patient/123
Patient/124

即使Request Body完全相同,Server也不一定知道這是重送,還是真的要建立兩筆資料。

因此,POST一般不是Idempotent Method。

Idempotent可以理解為:

將相同Request執行一次或多次,預期Server的最終資源狀態相同。

一般POST create重複執行可能建立多筆Resource,所以操作時要小心網路重送及重複建檔問題。

FHIR也提供Conditional Create等機制協助避免重複建立,但屬於較進階內容。


三、PUT:更新指定Resource

PUT在FHIR中常用來更新指定id的Resource。

例如,要更新:

Patient/patient-001

可以送出:

PUT https://hospital.example.org/fhir/Patient/patient-001
Content-Type: application/fhir+json
Accept: application/fhir+json

Request Body:

{
  "resourceType": "Patient",
  "id": "patient-001",
  "active": true,
  "name": [
    {
      "text": "王小明",
      "family": "王",
      "given": [
        "小明"
      ]
    }
  ],
  "gender": "male",
  "birthDate": "2000-01-01",
  "telecom": [
    {
      "system": "phone",
      "value": "0900-000-001",
      "use": "mobile"
    }
  ]
}

這個Request代表:

將Patient/patient-001更新為Request Body所提供的內容。


PUT不是只修改一個欄位

初學時可能會認為,如果只想修改電話,就只需要送出:

{
  "telecom": [
    {
      "system": "phone",
      "value": "0900-000-001"
    }
  ]
}

但FHIR的update通常會將Request Body視為Resource的新內容,而不是只修改其中一個欄位。

如果省略原本的姓名、生日或其他資料,這些欄位可能從更新後的Resource中消失,或Request可能因資料不完整而失敗。

因此,使用PUT更新前,常見流程是:

  1. 先GET目前的Resource。
  2. 保留需要存在的原始欄位。
  3. 修改目標欄位。
  4. 將完整Resource透過PUT送回。
  5. 再次GET確認結果。

如果只想修改部分欄位,HTTP另有PATCH方法,但FHIR Server不一定支援,格式也需要依照Server能力及規範使用。


PUT成功時的狀態碼

如果更新既有Resource成功,Server通常可能回傳:

200 OK

或在沒有回傳內容時使用:

204 No Content

Server也可能更新Resource版本:

"meta": {
  "versionId": "2"
}

如果Server允許使用PUT在指定位置建立原本不存在的Resource,成功時可能回傳:

201 Created

不過,不是每台FHIR Server都允許這種行為。


PUT的id要一致

假設Request URL是:

/Patient/patient-001

那麼Request Body中的id也應該是:

"id": "patient-001"

如果URL指定的是patient-001,Body卻寫成:

"id": "patient-002"

Server通常應該拒絕這項不一致的Request。

Resource類型也必須正確。傳送到:

/Patient/patient-001

的Body不能是:

{
  "resourceType": "Observation"
}

PUT具有Idempotent概念

如果將完全相同的PUT Request重複傳送:

PUT /Patient/patient-001

預期最終的Resource內容仍然相同,不會像一般POST create一樣,每次都建立新的Patient。

Server可能留下不同的歷史版本或操作紀錄,但目標Resource的最終內容應保持一致。

因此,PUT被視為Idempotent Method。


四、DELETE:刪除Resource

DELETE用來要求Server刪除指定Resource。

例如:

DELETE https://hospital.example.org/fhir/Patient/patient-001

這個Request代表:

請刪除Patient/patient-001。

DELETE通常不需要Request Body。

如果刪除成功,Server可能回傳:

200 OK

或:

204 No Content

實際回應會依FHIR Server而異。


DELETE後資料一定完全消失嗎?

不一定。

FHIR Server可能採用不同的刪除方式,例如:

  • Resource無法再透過一般read取得
  • 保留歷史版本
  • 以刪除標記取代實體移除
  • 因稽核、法規或系統政策保留紀錄
  • 不允許刪除特定類型Resource

刪除後再次讀取:

GET /Patient/patient-001

可能收到:

404 Not Found

或:

410 Gone

兩者概念不同:

  • 404 Not Found:Server找不到目前的Resource。
  • 410 Gone:Server知道Resource曾經存在,但目前已被刪除。

不是所有Server都會使用完全相同的回應方式。


DELETE也具有Idempotent概念

第一次執行:

DELETE /Patient/patient-001

Resource被刪除。

第二次執行相同Request時,Server可能回傳404或410,因為Resource已不存在。

雖然兩次Response的狀態碼可能不同,但Server中的最終資源狀態都一樣:

Patient/patient-001處於已刪除或無法取得的狀態。

因此,DELETE也被視為Idempotent Method。


四種方法一次比較

項目 GET POST PUT DELETE
主要用途 讀取或搜尋 建立Resource 更新指定Resource 刪除Resource
常見URL /Patient/123 /Patient /Patient/123 /Patient/123
通常有Request Body嗎? 沒有 通常沒有
誰決定id? 已知id 通常由Server分配 URL指定id URL指定id
是否修改Resource?
是否Idempotent? 一般create不是
常見成功碼 200 201 200、201或204 200或204

常見HTTP狀態碼

Status Code由三位數字組成,可以先依照第一個數字分類:

範圍 類別
1xx 資訊回應
2xx Request成功
3xx 重新導向
4xx Client端Request問題
5xx Server端問題

FHIR API操作中,較常遇到2xx、4xx及5xx。


200 OK

表示Request成功。

常見情境:

  • GET成功讀取Resource
  • PUT成功更新Resource
  • DELETE成功且Server回傳內容
200 OK

201 Created

表示新的Resource建立成功。

常見情境:

  • POST成功建立Resource
  • PUT在Server允許的情況下建立指定id的Resource
201 Created

通常可以從Location Header得知新Resource的位置。


204 No Content

表示Request成功,但Response沒有Body。

例如,更新或刪除成功後,Server可能只回傳:

204 No Content

看到空白Body不一定代表失敗,還要一起查看Status Code。


400 Bad Request

表示Request有問題,Server無法依照內容處理。

可能原因:

  • JSON語法錯誤
  • Resource結構錯誤
  • 缺少必要資料
  • URL參數格式錯誤
  • URL中的id與Body中的id不一致
  • 傳送錯誤的Resource類型
400 Bad Request

401 Unauthorized

通常表示Client尚未提供有效的身分驗證資訊。

可能原因:

  • 沒有Access Token
  • Token已過期
  • Token格式錯誤
  • 登入資訊無效
401 Unauthorized

雖然英文是Unauthorized,但實務上通常與「尚未完成有效身分驗證」有關。


403 Forbidden

表示Server已經知道Client的身分,但Client沒有執行該操作的權限。

例如:

  • 可以讀取Patient,但不能刪除Patient
  • 只能查看自己的資料
  • 沒有存取特定科別資料的權限
403 Forbidden

可以簡化區分:

狀態碼 基本概念
401 尚未通過有效身分驗證
403 已辨識身分,但權限不足

404 Not Found

表示找不到指定Resource或Endpoint。

例如:

GET /Patient/not-exist

可能回傳:

404 Not Found

可能原因包括:

  • Resource id錯誤
  • Resource不存在
  • URL路徑錯誤
  • Server不提供該Endpoint
  • 部分系統基於安全考量,不透露Resource是否存在

405 Method Not Allowed

表示URL可能存在,但不允許使用目前的HTTP方法。

例如,FHIR Server允許讀取Patient,卻不允許刪除:

DELETE /Patient/patient-001

可能回傳:

405 Method Not Allowed

409 Conflict

表示Request和Server目前狀態發生衝突。

例如:

  • 資料版本衝突
  • 唯一識別資料重複
  • 更新操作與Server目前狀態不相容
409 Conflict

422 Unprocessable Entity

表示Server能理解Request格式,但內容無法通過處理或驗證。

例如:

  • Resource不符合Profile
  • 欄位值不符合規則
  • 必填欄位缺失
  • 代碼不符合指定ValueSet
422 Unprocessable Entity

不同FHIR Server對400和422的使用方式可能略有差異,需要查看Response Body及Server文件。


500 Internal Server Error

表示Server處理Request時發生未預期錯誤。

500 Internal Server Error

這不一定代表Client完全沒有問題,但主要表示Server無法正常完成處理。

如果持續發生,通常需要查看Server Log或聯絡系統管理人員。


FHIR的OperationOutcome

FHIR Server遇到錯誤時,可能回傳OperationOutcome Resource,提供較詳細的問題資訊。

例如:

{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "invalid",
      "diagnostics": "Patient.id does not match the id in the request URL."
    }
  ]
}

常見欄位包括:

欄位 用途
severity 問題嚴重程度
code 問題類型
details 問題說明
diagnostics 診斷或技術訊息
locationexpression 發生問題的位置

看到錯誤時,不應只看Status Code,也要閱讀OperationOutcome中的內容。

例如,同樣是400 Bad Request,真正原因可能是:

  • JSON少了一個逗號
  • Resource類型錯誤
  • id不一致
  • 欄位資料型別錯誤

OperationOutcome可以提供更明確的線索。


操作前先查看CapabilityStatement

不是每一台FHIR Server都支援:

  • 所有Resource
  • 所有搜尋參數
  • POST create
  • PUT update
  • DELETE
  • PATCH
  • 歷史版本
  • 條件式操作

可以先呼叫:

GET /metadata

取得CapabilityStatement,查看Server提供的能力。

因此,某個Request失敗不一定代表HTTP方法寫錯,也可能是該Server沒有開放這項功能。


安全與資料注意事項

POST、PUT及DELETE都可能改變Server中的資料,操作前要特別注意:

  • 確認使用的是測試環境。
  • 不要在公開Server放入真實病人資料。
  • 確認URL及Resource id。
  • 建立資料後記下Server分配的id。
  • 更新前先備份或GET目前內容。
  • 刪除前確認目標Resource。
  • 不要公開Access Token。
  • 不要對正式醫療系統進行未授權測試。

本系列實作只會使用公開測試環境及虛構資料。


今日練習

請判斷以下需求應該使用哪一種HTTP方法。

需求一:讀取Patient/123

GET /Patient/123

答案是GET。

需求二:建立一筆新Patient,讓Server分配id

POST /Patient

答案是POST。

需求三:更新Patient/123的內容

PUT /Patient/123

答案是PUT。

需求四:刪除Patient/123

DELETE /Patient/123

答案是DELETE。

需求五:依照姓名搜尋Patient

GET /Patient?name=王小明

答案仍然是GET,因為這是讀取及搜尋資料。


今日小結

今天認識了FHIR RESTful API中四種常見HTTP方法:

  • GET:讀取或搜尋Resource
  • POST:由Server分配id並建立新Resource
  • PUT:更新指定id的Resource
  • DELETE:刪除指定Resource

也認識了常見狀態碼,包括:

  • 200 OK
  • 201 Created
  • 204 No Content
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 405 Method Not Allowed
  • 409 Conflict
  • 422 Unprocessable Entity
  • 500 Internal Server Error

我認為今天最重要的觀念是:

Request完成後,不能只看Response Body有沒有資料,也要一起查看Status Code、Headers及OperationOutcome。

下一篇將正式開始實作,安裝Postman並送出第一個API Request,實際觀察方法、URL、Headers、Status及Body分別出現在哪裡。

明日預告

Day 15|安裝Postman並送出第一個API請求

參考資料

  1. HL7 FHIR R4:RESTful API
    https://hl7.org/fhir/R4/http.html

  2. HL7 FHIR R4:Create
    https://hl7.org/fhir/R4/http.html#create

  3. HL7 FHIR R4:Update
    https://hl7.org/fhir/R4/http.html#update

  4. HL7 FHIR R4:Delete
    https://hl7.org/fhir/R4/http.html#delete

  5. HL7 FHIR R4:OperationOutcome
    https://hl7.org/fhir/R4/operationoutcome.html

  6. RFC 9110:HTTP Semantics
    https://www.rfc-editor.org/rfc/rfc9110


上一篇
Day 13|REST API是什麼?用餐廳點餐來理解
下一篇
Day 15|安裝Postman並送出第一個API請求
系列文
《醫資生的 FHIR 30日入門:用 Postman 讀懂醫療資料交換》30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言